use pest::Parser;
use pest::iterators::Pair;
use pest_derive::Parser;
use std::fmt::Display;

use crate::position::Position;
use crate::sem_table::{ArrayType, FunctionType, TableManager, Type};

#[derive(Parser)]
#[grammar = "parser.pest"]
pub struct SysYParser;

#[repr(u8)]
#[derive(Clone)]
pub enum ErrorType {
    IdentUndefined = 1,
    FunctionUndefined = 2,
    IdentRedefined = 3,
    FunctionRedfined = 4,
    AssignTypeUnmatched = 5,
    ExpTypeUnmatched = 6,
    ReturnedTypeUnmatched = 7,
    FunctionArgsError = 8,
    ArrayIndexError = 9,
    VeribalCalledError = 10,
    LValError = 11,
}

impl Display for ErrorType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.clone() as u8)
    }
}

pub struct SematicError {
    position: Position,
    error_type: ErrorType,
    message: String,
}

impl Display for SematicError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Error type {} at Line {}:{}",
            self.error_type, self.position.line, self.message
        )
    }
}

pub struct Analyzer {
    table_manager: TableManager,
    error_list: Vec<SematicError>,
    current_type: Type,
    current_ret_type: Type,
}

impl Analyzer {
    fn new() -> Self {
        Self {
            table_manager: TableManager::new(),
            error_list: Vec::new(),
            current_type: Type::Int,
            current_ret_type: Type::Void,
        }
    }

    fn add_error(&mut self, e: SematicError) {
        self.error_list.push(e);
    }

    fn scope_push(&mut self) {
        self.table_manager.scope_push();
    }

    fn scope_pop(&mut self) {
        self.table_manager.scope_pop();
    }

    fn add_sympol(&mut self, name: String, ty: Type) {
        self.table_manager.sympol_push(name, ty);
    }

    fn find(&self, name: &str) -> Option<Type> {
        self.table_manager.find(name)
    }

    fn check(&self, name: &str) -> bool {
        self.table_manager.check(name)
    }
}

pub fn sematic_analyze(input: &str) -> Vec<SematicError> {
    let mut a = Analyzer::new();
    let program = SysYParser::parse(Rule::program, input)
        .unwrap()
        .next()
        .unwrap();
    visit_program(program, &mut a);
    a.error_list
}

fn visit_program(pair: Pair<Rule>, a: &mut Analyzer) {
    // add scope for global
    a.scope_push();
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::comp_unit => visit_comp_unit(inner, a),
            Rule::EOI => (),
            _ => unreachable!(),
        }
    }
    // remove scope for global
    a.scope_pop();
}

fn visit_comp_unit(pair: Pair<Rule>, a: &mut Analyzer) {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::func_def => visit_func_def(inner, a),
        Rule::decl => visit_decl(inner, a),
        _ => unreachable!(),
    }
}

fn visit_decl(pair: Pair<Rule>, a: &mut Analyzer) {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::const_decl => visit_const_decl(inner, a),
        Rule::var_decl => visit_var_decl(inner, a),
        _ => unreachable!(),
    }
}

fn visit_const_decl(pair: Pair<Rule>, a: &mut Analyzer) {
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::r#const => (),
            Rule::b_type => visit_b_type(inner, a),
            Rule::const_def => visit_const_def(inner, a),
            Rule::comma => (),
            Rule::semicolon => (),
            _ => unreachable!(),
        }
    }
}

fn visit_b_type(pair: Pair<Rule>, a: &mut Analyzer) {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::int => a.current_type = Type::Int,
        _ => unreachable!(),
    }
}

fn visit_const_def(pair: Pair<Rule>, a: &mut Analyzer) {
    let mut ident: Option<String> = None;
    let mut pos: Option<Position> = None;
    let mut val_ty: Option<Type> = None;
    let mut num_array: usize = 0;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::ident => {
                ident = Some(inner.as_str().into());
                pos = Some(inner.line_col().into());
            }
            Rule::l_brackt => (),
            Rule::const_exp => num_array += 1,
            Rule::r_brackt => (),
            Rule::assign => (),
            Rule::const_init_val => val_ty = get_const_init_val_type(inner, a),
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    let pos = pos.unwrap();
    // check redefined
    if a.check(&ident) {
        // error redefined
        let err = SematicError {
            position: pos,
            error_type: ErrorType::IdentRedefined,
            message: format!("{} redefined", ident),
        };
        a.add_error(err);
        return;
    }
    let ty = if num_array == 0 {
        Type::Int
    } else {
        let array_type = ArrayType {
            contained: Box::new(Type::Int),
            num_elements: num_array,
        };
        Type::Array(array_type)
    };
    // compare lval type and val type
    if let Some(val_ty) = val_ty {
        if let Type::Array(_) = val_ty && Type::Int == ty {
            // error unmatched type
            let err = SematicError {
                position: pos,
                error_type: ErrorType::ExpTypeUnmatched,
                message: format!("{} init val type error", ident),
            };
            a.add_error(err);
            return;
        } else if let Type::Array(_) = ty && Type::Int == val_ty {
            // error unmatched type
            let err = SematicError {
                position: pos,
                error_type: ErrorType::ExpTypeUnmatched,
                message: format!("{} init val type error", ident),
            };
            a.add_error(err);
            return;
        }
    }
    a.add_sympol(ident, ty);
}

fn get_const_init_val_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut ty: Option<Type> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::exp => ty = get_exp_type(inner, a),
            Rule::l_brace => ty = Some(Type::Array(ArrayType { contained: Box::new(Type::Int), num_elements: 1 })),
            Rule::init_val => (),
            Rule::comma => (),
            Rule::r_brace => (),
            _ => unreachable!(),
        }
    }
    ty
}

fn visit_var_decl(pair: Pair<Rule>, a: &mut Analyzer) {
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::b_type => (),
            Rule::var_def => visit_var_def(inner, a),
            Rule::comma => (),
            Rule::semicolon => (),
            _ => unreachable!(),
        }
    }
}

fn visit_var_def(pair: Pair<Rule>, a: &mut Analyzer) {
    let mut ident: Option<String> = None;
    let mut pos: Option<Position> = None;
    let mut val_ty: Option<Type> = None;
    let mut num_array: usize = 0;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::ident => {
                ident = Some(inner.as_str().into());
                pos = Some(inner.line_col().into());
            }
            Rule::l_brackt => (),
            Rule::const_exp => num_array += 1,
            Rule::r_brackt => (),
            Rule::assign => (),
            Rule::init_val => val_ty = get_init_val_type(inner, a),
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    let pos = pos.unwrap();
    // check redefined
    if a.check(&ident) {
        let err = SematicError {
            position: pos,
            error_type: ErrorType::IdentRedefined,
            message: format!("{} redefined", ident),
        };
        a.add_error(err);
        return;
    }
    let ty = if num_array == 0 {
        Type::Int
    } else {
        let array_type = ArrayType {
            contained: Box::new(Type::Int),
            num_elements: num_array,
        };
        Type::Array(array_type)
    };
    // compare lval type and val type
    if let Some(val_ty) = val_ty {
        if let Type::Array(_) = val_ty && Type::Int == ty {
            // error unmatched type
            let err = SematicError {
                position: pos,
                error_type: ErrorType::ExpTypeUnmatched,
                message: format!("{} init val type error", ident),
            };
            a.add_error(err);
            return;
        } else if let Type::Array(_) = ty && Type::Int == val_ty {
            // error unmatched type
            let err = SematicError {
                position: pos,
                error_type: ErrorType::ExpTypeUnmatched,
                message: format!("{} init val type error", ident),
            };
            a.add_error(err);
            return;
        }
    }
    a.add_sympol(ident, ty);
}

fn get_init_val_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut ty: Option<Type> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::exp => ty = get_exp_type(inner, a),
            Rule::l_brace => ty = Some(Type::Array(ArrayType { contained: Box::new(Type::Int), num_elements: 1 })),
            Rule::init_val => (),
            Rule::comma => (),
            Rule::r_brace => (),
            _ => unreachable!(),
        }
    }
    ty
}

fn visit_func_def(pair: Pair<Rule>, a: &mut Analyzer) {
    let mut func_type: Option<Type> = None;
    let mut ident: Option<String> = None;
    let mut pos: Option<Position> = None;
    let mut params: Vec<(String, Type)> = Vec::new();
    let mut block: Option<Pair<Rule>> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::func_type => func_type = Some(get_func_type(inner)),
            Rule::ident => {
                ident = Some(inner.as_str().into());
                pos = Some(inner.line_col().into());
            }
            Rule::l_paren => (),
            Rule::func_f_params => {
                params = get_params_type(inner, a);
            }
            Rule::r_paren => (),
            Rule::block => block = Some(inner),
            _ => unreachable!(),
        }
    }
    let func_type = func_type.unwrap();
    let ident = ident.unwrap();
    let pos = pos.unwrap();
    let block = block.unwrap();
    // check redefined
    if a.check(&ident) {
        // error redefined
        let err = SematicError {
            position: pos,
            error_type: ErrorType::FunctionRedfined,
            message: format!("{} redefined", ident),
        };
        a.add_error(err);
        return;
    }
    // get params_type
    let params_type = params.iter().map(|s| s.1.clone()).collect();
    let func_ty = FunctionType {
        ret_ty: Box::new(func_type.clone()),
        params_type,
    };
    let ty = Type::Function(func_ty);
    a.add_sympol(ident, ty);
    // check arguments
    a.scope_push();
    for (name, ty) in &params {
        if a.check(name) {
            // redefined
            let err = SematicError {
                position: pos,
                error_type: ErrorType::IdentRedefined,
                message: format!("{} redefined", name),
            };
            a.add_error(err);
            break;
        }
        a.add_sympol(name.clone(), ty.clone());
    }
    // store return type
    a.current_ret_type = func_type;
    visit_block(block, a);
    // pop for function arguments
    a.scope_pop();
}

fn get_func_type(pair: Pair<Rule>) -> Type {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::void => Type::Void,
        Rule::int => Type::Int,
        _ => unreachable!(),
    }
}

fn get_params_type(pair: Pair<Rule>, a: &mut Analyzer) -> Vec<(String, Type)> {
    let mut types: Vec<(String, Type)> = Vec::new();
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::func_f_param => types.push(get_param_type(inner, a)),
            Rule::comma => (),
            _ => unreachable!(),
        }
    }
    types
}

fn get_param_type(pair: Pair<Rule>, _: &mut Analyzer) -> (String, Type) {
    let mut array_num: usize = 0;
    let mut ident: Option<String> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::b_type => (),
            Rule::ident => {
                ident = Some(inner.as_str().into());
            }
            Rule::l_brackt => (),
            Rule::r_brackt => (),
            Rule::exp => array_num += 1,
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    let ty = if array_num == 0 {
        Type::Int
    } else {
        let array_type = ArrayType {
            contained: Box::new(Type::Int),
            num_elements: array_num,
        };
        Type::Array(array_type)
    };
    // add ident
    (ident, ty)
}

fn visit_block(pair: Pair<Rule>, a: &mut Analyzer) {
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::l_brace => a.scope_push(),
            Rule::block_item => visit_block_item(inner, a),
            Rule::r_brace => a.scope_pop(),
            _ => unreachable!(),
        }
    }
}

fn visit_block_item(pair: Pair<Rule>, a: &mut Analyzer) {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::stmt => visit_stmt(inner, a),
        Rule::decl => visit_decl(inner, a),
        _ => unreachable!(),
    }
}

fn visit_stmt(pair: Pair<Rule>, a: &mut Analyzer) {
    enum State {
        If,
        While,
        Break,
        Continue,
        Return(Position),
        LVal(Position),
        Exp,
    }
    let mut state = State::Exp;
    let mut l_val_ty: Option<Type> = None;
    let mut exp_ty: Option<Type> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::r#if => state = State::If,
            Rule::l_paren => (),
            Rule::cond => {
                let pos = inner.line_col().into();
                match get_cond_type(inner, a) {
                    Some(ty) => {
                        // must int
                        if ty != Type::Int {
                            let err = SematicError {
                                position: pos,
                                error_type: ErrorType::ExpTypeUnmatched,
                                message: "cond not int".into(),
                            };
                            a.add_error(err);
                            return;
                        }
                    }
                    None => return,
                }
            }
            Rule::r_paren => (),
            Rule::stmt => visit_stmt(inner, a),
            Rule::r#else => (),
            Rule::r#while => state = State::While,
            Rule::r#break => state = State::Break,
            Rule::semicolon => (),
            Rule::r#continue => state = State::Continue,
            Rule::r#return => state = State::Return(inner.line_col().into()),
            Rule::exp => exp_ty = get_exp_type(inner, a),
            Rule::l_val => l_val_ty = get_l_val_type(inner, a),
            Rule::assign => state = State::LVal(inner.line_col().into()),
            Rule::block => visit_block(inner, a),
            _ => unreachable!(),
        }
    }
    match state {
        State::Return(pos) => {
            if let Some(exp_ty) = exp_ty {
                // only int
                if exp_ty != Type::Int || a.current_ret_type != Type::Int {
                    // error function return type
                    let err = SematicError {
                        position: pos,
                        error_type: ErrorType::ReturnedTypeUnmatched,
                        message: "return type unmatched".into(),
                    };
                    a.add_error(err);
                }
            } else if a.current_ret_type != Type::Void {
                // error function return type
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::ReturnedTypeUnmatched,
                    message: "return type unmatched".into(),
                };
                a.add_error(err);
            }
        }
        State::LVal(pos) => {
            // check l_val type and exp type
            let l_val_ty = match l_val_ty {
                Some(ty) => ty,
                None => return,
            };
            let exp_ty = match exp_ty {
                Some(ty) => ty,
                None => return,
            };
            if l_val_ty != exp_ty {
                // error type unmatched
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::AssignTypeUnmatched,
                    message: format!("LVal: {:?}, Exp: {:?}", l_val_ty, exp_ty),
                };
                a.add_error(err);
            }
        }
        _ => (),
    }
}

fn get_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::add_exp => get_add_exp_type(inner, a),
        _ => unreachable!(),
    }
}

fn get_cond_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::l_or_exp => get_l_or_exp(inner, a),
        _ => unreachable!(),
    }
}

fn get_l_val_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut ident: Option<String> = None;
    let mut pos: Option<Position> = None;
    let mut array_num: usize = 0;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::ident => {
                ident = Some(inner.as_str().into());
                pos = Some(inner.line_col().into());
            }
            Rule::l_brackt => array_num += 1,
            Rule::exp => {
                // check exp type, must be int
            }
            Rule::r_brackt => (),
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    let pos = pos.unwrap();
    let mut ident_array_num: usize = 0;
    // check ident defined
    if let Some(ty) = a.find(&ident) {
        // type must be int or array
        match ty {
            Type::Int => (),
            Type::Array(array_type) => ident_array_num = array_type.num_elements,
            Type::Function(_) => {
                // type error
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::LValError,
                    message: format!("{} is a function", ident),
                };
                a.add_error(err);
                return Some(Type::Void);
            }
            _ => (),
        }
        // check array num
        if array_num > ident_array_num {
            // array error
            let err = SematicError {
                position: pos,
                error_type: ErrorType::ArrayIndexError,
                message: format!("{} is over {}", ident, ident_array_num),
            };
            a.add_error(err);
            return None;
        }
    } else {
        // undefined
        let err = SematicError {
            position: pos,
            error_type: ErrorType::IdentUndefined,
            message: format!("{} undefined", ident),
        };
        a.add_error(err);
        return None;
    }
    assert!(array_num <= ident_array_num);
    let array_num = ident_array_num - array_num;
    Some(if array_num == 0 {
        Type::Int
    } else {
        let array_type = ArrayType {
            contained: Box::new(Type::Int),
            num_elements: array_num,
        };
        Type::Array(array_type)
    })
}

fn get_primary_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut ty: Option<Type> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::l_paren => (),
            Rule::exp => ty = get_exp_type(inner, a),
            Rule::r_paren => (),
            Rule::l_val => ty = get_l_val_type(inner, a),
            Rule::number => ty = Some(Type::Int),
            _ => unreachable!(),
        }
    }
    ty
}

fn get_unary_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut ident: Option<String> = None;
    let mut pos: Option<Position> = None;
    let mut r_params: Option<Vec<Type>> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::ident => {
                ident = Some(inner.as_str().into());
                pos = Some(inner.line_col().into());
            }
            Rule::l_paren => (),
            Rule::func_r_params => r_params = get_r_params_type(inner, a),
            Rule::r_paren => (),
            Rule::primary_exp => return get_primary_exp_type(inner, a),
            Rule::unary_op => (),
            Rule::unary_exp => return get_unary_exp_type(inner, a),
            _ => unreachable!(),
        }
    }
    let ident = ident.unwrap();
    let pos = pos.unwrap();
    if let Some(ty) = a.find(&ident) {
        if let Type::Function(func) = ty {
            if let Some(r_params) = r_params {
                // check arguments
                if func.params_type != r_params {
                    // arguments error
                    let err = SematicError {
                        position: pos,
                        error_type: ErrorType::FunctionArgsError,
                        message: format!("{} arguments error", ident),
                    };
                    a.add_error(err);
                    None
                } else {
                    Some(*func.ret_ty.clone())
                }
            } else {
                Some(*func.ret_ty.clone())
            }
        } else {
            // error not function
            let err = SematicError {
                position: pos,
                error_type: ErrorType::VeribalCalledError,
                message: format!("{} not function", ident),
            };
            a.add_error(err);
            None
        }
    } else {
        // undefined function
        let err = SematicError {
            position: pos,
            error_type: ErrorType::FunctionUndefined,
            message: format!("{} function undefined", ident),
        };
        a.add_error(err);
        None
    }
}

fn get_r_params_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Vec<Type>> {
    let mut params_type: Vec<Type> = Vec::new();
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::exp => {
                if let Some(ty) = get_exp_type(inner, a) {
                    params_type.push(ty);
                } else {
                    return None;
                }
            }
            Rule::comma => (),
            _ => unreachable!(),
        }
    }
    Some(params_type)
}

fn get_mul_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut types: Vec<Type> = Vec::new();
    let mut pos: Option<Position> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::unary_exp => {
                pos = Some(inner.line_col().into());
                if let Some(ty) = get_unary_exp_type(inner, a) {
                    types.push(ty);
                } else {
                    return None;
                }
            }
            Rule::mul => (),
            Rule::div => (),
            Rule::r#mod => (),
            _ => unreachable!(),
        }
    }
    let pos = pos.unwrap();
    if types.len() == 1 {
        Some(types.last().unwrap().clone())
    } else {
        // check all types must be int
        for ty in &types {
            if *ty != Type::Int {
                // error type
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::ExpTypeUnmatched,
                    message: "exp type error".into(),
                };
                a.add_error(err);
                return None;
            }
        }
        Some(types.last().unwrap().clone())
    }
}

fn get_add_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut types: Vec<Type> = Vec::new();
    let mut pos: Option<Position> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::mul_exp => {
                pos = Some(inner.line_col().into());
                if let Some(ty) = get_mul_exp_type(inner, a) {
                    types.push(ty);
                } else {
                    return None;
                }
            }
            Rule::plus => (),
            Rule::minus => (),
            _ => unreachable!(),
        }
    }
    let pos = pos.unwrap();
    if types.len() == 1 {
        Some(types.last().unwrap().clone())
    } else {
        // check all types must be int
        for ty in &types {
            if *ty != Type::Int {
                // error type
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::ExpTypeUnmatched,
                    message: "exp type error".into(),
                };
                a.add_error(err);
                return None;
            }
        }
        Some(types.last().unwrap().clone())
    }
}

fn get_rel_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut types: Vec<Type> = Vec::new();
    let mut pos: Option<Position> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::add_exp => {
                pos = Some(inner.line_col().into());
                if let Some(ty) = get_add_exp_type(inner, a) {
                    types.push(ty);
                } else {
                    return None;
                }
            }
            Rule::le => (),
            Rule::ge => (),
            Rule::lt => (),
            Rule::gt => (),
            _ => unreachable!(),
        }
    }
    let pos = pos.unwrap();
    if types.len() == 1 {
        Some(types.last().unwrap().clone())
    } else {
        // check all types must be int
        for ty in &types {
            if *ty != Type::Int {
                // error type
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::ExpTypeUnmatched,
                    message: "exp type error".into(),
                };
                a.add_error(err);
                return None;
            }
        }
        Some(types.last().unwrap().clone())
    }
}

fn get_eq_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut types: Vec<Type> = Vec::new();
    let mut pos: Option<Position> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::rel_exp => {
                pos = Some(inner.line_col().into());
                if let Some(ty) = get_rel_exp_type(inner, a) {
                    types.push(ty);
                } else {
                    return None;
                }
            }
            Rule::eq => (),
            Rule::neq => (),
            _ => unreachable!(),
        }
    }
    let pos = pos.unwrap();
    if types.len() == 1 {
        Some(types.last().unwrap().clone())
    } else {
        // check all types must be int
        for ty in &types {
            if *ty != Type::Int {
                // error type
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::ExpTypeUnmatched,
                    message: "exp type error".into(),
                };
                a.add_error(err);
                return None;
            }
        }
        Some(types.last().unwrap().clone())
    }
}

fn get_l_and_exp_type(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut types: Vec<Type> = Vec::new();
    let mut pos: Option<Position> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::eq_exp => {
                pos = Some(inner.line_col().into());
                if let Some(ty) = get_eq_exp_type(inner, a) {
                    types.push(ty);
                } else {
                    return None;
                }
            }
            Rule::and => (),
            _ => unreachable!(),
        }
    }
    let pos = pos.unwrap();
    if types.len() == 1 {
        Some(types.last().unwrap().clone())
    } else {
        // check all types must be int
        for ty in &types {
            if *ty != Type::Int {
                // error type
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::ExpTypeUnmatched,
                    message: "exp type error".into(),
                };
                a.add_error(err);
                return None;
            }
        }
        Some(types.last().unwrap().clone())
    }
}

fn get_l_or_exp(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let mut types: Vec<Type> = Vec::new();
    let mut pos: Option<Position> = None;
    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::l_and_exp => {
                pos = Some(inner.line_col().into());
                if let Some(ty) = get_l_and_exp_type(inner, a) {
                    types.push(ty);
                } else {
                    return None;
                }
            }
            Rule::or => (),
            _ => unreachable!(),
        }
    }
    let pos = pos.unwrap();
    if types.len() == 1 {
        Some(types.last().unwrap().clone())
    } else {
        // check all types must be int
        for ty in &types {
            if *ty != Type::Int {
                // error type
                let err = SematicError {
                    position: pos,
                    error_type: ErrorType::ExpTypeUnmatched,
                    message: "exp type error".into(),
                };
                a.add_error(err);
                return None;
            }
        }
        Some(types.last().unwrap().clone())
    }
}

/*
fn get_const_exp(pair: Pair<Rule>, a: &mut Analyzer) -> Option<Type> {
    let inner = pair.into_inner().next().unwrap();
    match inner.as_rule() {
        Rule::add_exp => get_add_exp_type(inner, a),
        _ => unreachable!(),
    }
}
*/
